# @desc 感知器
# @url https://www.zybuluo.com/hanbingtao/note/433855
class Perceptron
  def initialize input_num, activator
    @activator = activator
    @input_num = input_num
    @weights = input_num.times.collect {0}
    @bias = 0.0
  end

  def to_s
    "weights\t:#{@weights}\nbias\t:#{@bias}"
  end

  def predict input_vec
    sum = 0
    @weights.each_with_index do |i, index|
      sum += i * input_vec[index]
    end
    @activator.activate(sum + @bias)
  end

  def train input_vecs, labels, iteration, rate
    iteration.times do |i|
      one_iteration input_vecs, labels, rate
    end
  end

  def one_iteration input_vecs, labels, rate
    samples = []
    input_vecs.each_with_index do |v, index|
      samples.push([v, labels[index]])
    end
    samples.each do |a|
      out_put = predict(a[0])
      update_weights(a[0], out_put, a[1], rate)
    end
  end

  def update_weights input_vec, out_put, label, rate
    delta = label - out_put
    @weights.each_with_index do |v, index|
      @weights[index] += delta * input_vec[index] * rate
    end
    @bias += rate * delta
  end
end

def get_training_dataset
  input_vecs = [[1, 1], [0, 0], [1, 0], [0, 1]]
  labels = [1, 0, 0, 0]
  return input_vecs, labels
end

def train_and_perceptron
  p = Perceptron.new(2, Activator)
  input_vecs, labels = get_training_dataset
  p.train(input_vecs, labels, 10, 0.1)
  p
end

def main
  and_perception = train_and_perceptron
  puts and_perception

  puts "1 and 1 = #{and_perception.predict [1, 1]}"
  puts "0 and 0 = #{and_perception.predict [0, 0]}"
  puts "1 and 0 = #{and_perception.predict [1, 0]}"
  puts "0 and 1 = #{and_perception.predict [0, 1]}"
end

class Activator
  def self.activate x
    x > 0 ? 1 : 0
  end
end